package com.hilinos.common.util.excel;

import com.hilinos.common.util.DateUtils;
import com.hilinos.common.util.EntityUtils;
import lombok.Builder;
import lombok.Data;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.util.ObjectUtils;

import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class ExcelUtils {

    private List<String> sheetNameList = new ArrayList<>();
    private List<List<?>> datasList = new ArrayList<>();

    private ExcelUtils() {
    }

    public static synchronized ExcelUtils getInstance() {
        return new ExcelUtils();
    }

    /**
     * 添加sheet
     *
     * @param sheetName sheet名字
     * @param datas     数据列表，Object的字段可以添加@ExcelRowInfo注解来设置字段名字，也就是表格第一行
     * @return 当前类
     */
    public ExcelUtils addSheet(String sheetName, List<?> datas) {
        if (ObjectUtils.isEmpty(sheetName)) {
            sheetNameList.add("item" + sheetNameList.size());
        } else {
            sheetNameList.add(sheetName);
        }
        datasList.add(datas);
        return this;
    }

    /**
     * 设置sheet的名字
     *
     * @param datas 数据列表，Object的字段可以添加@ExcelField注解来设置字段名字，也就是表格第一行
     * @return 当前类
     */
    public ExcelUtils addSheet(List<?> datas) {
        return addSheet(null, datas);
    }

    /**
     * 导出到excel
     *
     * @param out 输出流
     */
    public ExcelUtils build(OutputStream out) throws Exception {
        XSSFWorkbook workbook = new XSSFWorkbook();
        XSSFCellStyle blodStyle = workbook.createCellStyle();
        XSSFFont font = workbook.createFont();
        font.setBold(true);
        blodStyle.setFont(font);
        for (int i = 0; i < sheetNameList.size(); i++) {
            String sheetName = sheetNameList.get(i);
            List<?> datas = datasList.get(i);
            if (ObjectUtils.isEmpty(datas)) {
                continue;
            }
            XSSFSheet sheet = workbook.createSheet(sheetName);
            XSSFRow titleRow = sheet.createRow(0);
            List<RowInfo> rowInfos = getRowInfos(datas.get(0).getClass());
            for (int lineNum = 0; lineNum < datas.size(); lineNum++) {
                Map<String, Object> data = EntityUtils.entity2Entity(datas.get(lineNum), Map.class);
                XSSFRow row = sheet.createRow(lineNum + 1);
                // 如果是第一次，则创建标题栏
                for (int rowNum = 0; rowNum < rowInfos.size(); rowNum++) {
                    RowInfo rowInfo = rowInfos.get(rowNum);
                    if (lineNum == 0) {
                        XSSFCell cell = titleRow.createCell(rowNum);
                        cell.setCellStyle(blodStyle);
                        cell.setCellValue(rowInfo.getName());
                    }
                    Object val = data.get(rowInfo.getKey());
                    String valStr = "";
                    if (!ObjectUtils.isEmpty(val)) {
                        if (val instanceof Date) {
                            valStr = DateUtils.getDateString((Date) val);
                        } else {
                            valStr = val + "";
                        }
                    }
                    sheet.setColumnWidth(rowNum, rowInfo.width * 64);
                    XSSFCell cell = row.createCell(rowNum);
                    cell.setCellValue(valStr);
                }
            }
        }
        workbook.write(out);
        workbook.close();
        return this;
    }

    private List<RowInfo> getRowInfos(Class dataCls) {
        Field[] fields = dataCls.getDeclaredFields();
        List<RowInfo> rowInfos = new ArrayList<>();
        if (!ObjectUtils.isEmpty(fields)) {
            for (Field field : fields) {
                ExcelRowInfo excelRowInfo = field.getDeclaredAnnotation(ExcelRowInfo.class);
                if (excelRowInfo != null) {
                    if (excelRowInfo.sort() <= 0) {
                        rowInfos.add(0, ExcelRowInfoToRowInfo(excelRowInfo, field));
                    } else {
                        boolean added = false;
                        for (int i = 0; i < rowInfos.size(); i++) {
                            RowInfo rowInfo = rowInfos.get(i);
                            if (excelRowInfo.sort() <= rowInfo.sort) {
                                rowInfos.add(i, ExcelRowInfoToRowInfo(excelRowInfo, field));
                                added = true;
                                break;
                            }
                        }
                        if (!added) {
                            rowInfos.add(ExcelRowInfoToRowInfo(excelRowInfo, field));
                        }
                    }
                }
            }
        }
        return rowInfos;
    }

    public static RowInfo ExcelRowInfoToRowInfo(ExcelRowInfo excelRowInfo, Field field) {
        return RowInfo.builder()
                .width(excelRowInfo.width())
                .name(excelRowInfo.name())
                .key(field.getName())
                .sort(excelRowInfo.sort())
                .build();
    }

    @Builder
    @Data
    public static class RowInfo {
        private int width;
        private String name;
        private String key;
        private int sort;
    }

}
